﻿using com.epam.indigo;
using System;
using System.Collections.Generic;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
using System.Text.RegularExpressions;
using SixLabors.ImageSharp.Processing;
using System.IO;

namespace IndigoNetImgHelper
{
    public class ChemMolecule
    {
        private Indigo ido = null;
        private string _molorsmiles = "";

        #region 构造函数
        public ChemMolecule() { }
        public ChemMolecule(Indigo _ido, IndigoObject io)
        {
            ido = _ido;
            _molorsmiles = io.molfile();
            _io = io;
        }
        #endregion

        #region 元素
        private IndigoObject _io = null;
        public IndigoObject IDO
        {
            get { return _io; }
        }
        public string MolFile()
        {
            if (_io != null)
            {
                _io.dearomatize();
                _io.layout();
                return _io.molfile();
            }
            return "";
        }
        public string CanonicalSmiles()
        {
            if (_io != null)
            {
                try
                {
                    var smiles = _io.canonicalSmiles();

                    if (!string.IsNullOrEmpty(smiles))
                    {
                        var pos = smiles.IndexOf(" |&");
                        if (pos != -1)
                        {
                            smiles = smiles.Substring(0, pos).Replace(" ", "");
                        }
                    }
                    return smiles;
                }
                catch { }
            }
            return "";
        }
        public string Smiles()
        {
            if (_io != null)
            {
                try
                {//无法计算具有重复单位的结构的质量
                    var smiles = _io.smiles();
                    if (!string.IsNullOrEmpty(smiles))
                    {
                        var pos = smiles.IndexOf(" |&");
                        if (pos != -1)
                        {
                            smiles = smiles.Substring(0, pos).Replace(" ", "");
                        }
                    }
                    return smiles;
                }
                catch { }
            }
            return "";
        }
        public string FormulaWithWhiteSpace()
        {
            if (_io != null)
            {
                var formula = _io.grossFormula();
                return formula;
            }
            return "";
        }
        public string Formula()
        {
            try
            {
                if (_io != null)
                {
                    var formula = _io.grossFormula();
                    if (!string.IsNullOrEmpty(formula))
                    {
                        formula = formula.Replace(" ", "");
                    }
                    return formula;
                }
            }
            catch { }
            return "";
        }
        public string FormulaHtml()
        {
            if (_io != null)
            {
                try
                {
                    var formula = _io.grossFormula();
                    if (!string.IsNullOrEmpty(formula))
                    {
                        string[] fa = formula.Split(' ');
                        string returnstr = "";
                        for (var i = 0; i < fa.Length; i++)
                        {
                            string str = fa[i];
                            string rstr = "";
                            bool foundnum = false;
                            for (var j = 0; j < str.Length; j++)
                            {
                                string c = str.Substring(j, 1);
                                System.Text.ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding();
                                int intC = (int)asciiEncoding.GetBytes(c)[0];
                                if ((intC >= 48 && intC <= 57))
                                {
                                    foundnum = true;
                                    //数字的
                                    if (j == 0)
                                    {
                                        //理论上不会到这一步
                                        rstr = "<sub>" + str.Substring(0) + "</sub>";
                                    }
                                    else
                                    {
                                        rstr = str.Substring(0, j) + "<sub>" + str.Substring(j) + "</sub>";
                                    }
                                    break;
                                }

                            }
                            if (!foundnum)
                            {
                                rstr += str;
                            }
                            returnstr += rstr;
                        }
                        return returnstr;
                    }
                }
                catch { }
                return "";
            }
            return "";
        }

        public string MolWeight()
        {
            if (_io != null)
            {
                try
                {//无法计算具有重复单位的结构的质量
                    return _io.molecularWeight().ToString();
                }
                catch { }
            }
            return "";
        }
        public string MonoisotopicMass()
        {
            if (_io != null)
            {
                try
                {
                    return _io.monoisotopicMass().ToString();
                }
                catch { }
            }
            return "";
        }
        public string MostAbundantMass()
        {
            if (_io != null)
            {
                try
                {
                    return _io.mostAbundantMass().ToString();
                }
                catch { }
            }
            return "";
        }
        public string InChI()
        {

            IndigoInchi indigo_inchi = new IndigoInchi(ido);
            if (_io != null)
            {
                try
                {
                    return indigo_inchi.getInchi(_io);
                }
                catch { }
            }
            return "";
        }
        public string InChIKey()
        {
            IndigoInchi indigo_inchi = new IndigoInchi(ido);
            if (_io != null)
            {
                try
                {
                    string inchi = indigo_inchi.getInchi(_io); ;
                    if (!string.IsNullOrEmpty(inchi))
                    {
                        return indigo_inchi.getInchiKey(inchi);
                    }
                }
                catch { }
            }
            return "";
        }
        public string ToString(string mimetype = "")
        {
            if (_io != null)
            {
                switch (mimetype)
                {
                    case "smiles":
                        return Smiles();

                    case "inchi":
                        return InChI();
                    default:
                        return _io.molfile();

                }
            }
            return "";
        }
        public bool IsEmpty
        {
            get
            {
                return _io == null;
            }
        }
        public static string FormatSMILES(string smiles)
        {
            if (!string.IsNullOrEmpty(smiles))
            {
                if (smiles.IndexOf("|&") != -1)
                {
                    return smiles.Substring(0, smiles.IndexOf("|&")).Trim();
                }
            }
            return smiles;
        }
        public static string FormatMol(string mol)
        {
            string[] lines = mol.Split('\n');
            string al = "";
            foreach (string a in lines)
            {
                RegexOptions options = RegexOptions.None;
                Regex regex = new Regex(@"[ ]{2,}", options);
                string tempo = "";
                tempo = a;
                string nstr = a;
                tempo = regex.Replace(tempo, @" ");
                string c = "#";
                if (tempo.Split(' ').Length == 8)
                {
                    nstr = nstr.Replace("0  0  1  0", "0  0  0  0");
                }
                string chars = "";
                foreach (char s in a)
                {
                    chars += Convert.ToInt32(s) + ",";
                }
                al += nstr + Environment.NewLine;
            }
            return al;
        }
        #endregion

        #region 生成结构式图片
        /// <summary>
        /// 生成结构式图片
        /// 常用参数详见：https://lifescience.opensource.epam.com/indigo_this/options/rendering-options.html
        /// </summary>
        /// <param name="w">宽度</param>
        /// <param name="h">高度</param>
        /// <param name="margin"></param>
        /// <param name="thickness">线条粗度</param>
        /// <returns></returns>
        public System.Drawing.Bitmap ToImage(int w = 500, int h = 500, int margin = 10, string thickness = "1.5", Dictionary<string, string> optipons = null, bool def = true)
        {
            if (margin <= 0) margin = 10;
            if (ido != null && _io != null)
            {
                if (optipons == null) optipons = new Dictionary<string, string>();
                IndigoRenderer ir = new IndigoRenderer(ido);
                if (optipons.Count > 0)
                {
                    foreach (var opt in optipons)
                    {
                        ido.setOption(opt.Key, opt.Value);
                    }
                    #region 默认参数
                    if (!optipons.ContainsKey("render-image-size"))
                    {
                        ido.setOption("render-image-size", w + "," + h);
                    }
                    if (!optipons.ContainsKey("render-margins"))
                    {
                        ido.setOption("render-margins", margin, margin);
                    }
                    if (!optipons.ContainsKey("render-relative-thickness"))
                    {
                        ido.setOption("render-relative-thickness", thickness);
                    }
                    #endregion
                }
                else
                {
                    ido.setOption("render-image-size", w + "," + h);
                    ido.setOption("render-margins", margin, margin);
                    ido.setOption("render-relative-thickness", thickness);
                }
                #region 默认参数
                if (def)
                {
                    if (!optipons.ContainsKey("molfile-saving-no-chiral"))
                    {
                        ido.setOption("molfile-saving-no-chiral", true);//不显示chiral 字样
                    }
                    if (!optipons.ContainsKey("render-stereo-style"))
                    {
                        ido.setOption("render-stereo-style", "none");
                    }
                    if (!optipons.ContainsKey("render-label-mode"))
                    {
                        ido.setOption("render-label-mode", "hetero");
                    }
                    if (!optipons.ContainsKey("render-superatom-mode"))
                    {
                        ido.setOption("render-superatom-mode", "collapse");
                    }
                    if (!optipons.ContainsKey("render-output-format"))
                    {
                        ido.setOption("render-output-format", "png");//图片格式
                    }
                    if (!optipons.ContainsKey("render-coloring"))
                    {
                        ido.setOption("render-coloring", true);//启用原子着色，例如氮为蓝色，氧为红色等
                    }
                    if (!optipons.ContainsKey("render-background-color"))
                    {
                        ido.setOption("render-background-color", "255,255,255");//背景颜色默认 白色
                    }
                    if (!optipons.ContainsKey("render-implicit-hydrogens-visible"))
                    {
                        ido.setOption("render-implicit-hydrogens-visible", true);//显示可见原子上的隐式氢
                    }
                    if (!optipons.ContainsKey("treat-x-as-pseudoatom"))
                    {
                        ido.setOption("treat-x-as-pseudoatom", "true");
                    }
                    if (!optipons.ContainsKey("render-highlight-thickness-enabled"))
                    {
                        ido.setOption("render-highlight-thickness-enabled", "true");
                    }

                    #region other 
                    //if (!optipons.ContainsKey("treat-x-as-pseudoatom"))
                    //{
                    //    ido.setOption("treat-x-as-pseudoatom", "true");
                    //}
                    //if (!optipons.ContainsKey("molfile-saving-no-chiral"))
                    //{
                    //    ido.setOption("molfile-saving-no-chiral", true);//不显示chiral 字样
                    //}
                    //if (!optipons.ContainsKey("ignore-stereochemistry-errors"))
                    //{
                    //    ido.setOption("ignore-stereochemistry-errors", true);
                    //}
                    //if (!optipons.ContainsKey("ignore-noncritical-query-features"))
                    //{
                    //    ido.setOption("ignore-noncritical-query-features", true);
                    //}
                    #endregion
                }
                #endregion
                System.Drawing.Bitmap bitmap = ir.renderToBitmap(_io);
                return bitmap;

            }
            return new System.Drawing.Bitmap(w, h);
        }

        #endregion

        #region 生成结构式图片
        /// <summary>
        /// 生成结构式图片
        /// 常用参数详见：https://lifescience.opensource.epam.com/indigo_this/options/rendering-options.html
        /// </summary>
        /// <param name="molorsmiles">mol或smiles</param>
        /// <param name="errmsg">返回错误信息</param>
        /// <param name="w">宽度 默认500</param>
        /// <param name="h">高度 默认500</param>
        /// <param name="margin"> 默认10</param>
        /// <param name="thickness">线条粗度 默认1.5</param>
        /// <param name="optipons">配置参数</param>
        /// <param name="def">是否默认参数 默认true</param>
        /// <param name="filename">生成图片名称，不含.png 默认当前时间</param>
        /// <param name="filepath">存放路径 默认当前根目录下images目录</param>
        /// <param name="watermarkpath">水印图片路径，无则说明无水印 默认无</param>
        /// <returns>生成图片的全路径，若为空则说明生成失败</returns>
        public string SaveImage(string molorsmiles, out string errmsg, int w = 500, int h = 500, int margin = 10, string thickness = "1.5", Dictionary<string, string> optipons = null, bool def = true, string filename = "structimg", string filepath = "", string watermarkpath = "")
        {
            errmsg = "";
            if (string.IsNullOrWhiteSpace(molorsmiles)) { errmsg = "Mol或Smiles文件为空，无法生成结构式图片！"; return null; }
            if (w <= 0) w = 500;
            if (h <= 0) h = 500;
            if (margin <= 0) margin = 10;
            if (string.IsNullOrWhiteSpace(thickness)) { thickness = "1.5"; }
            if (string.IsNullOrWhiteSpace(filepath)) { filepath = AppDomain.CurrentDomain.BaseDirectory + "images\\"; }
            if (string.IsNullOrWhiteSpace(filename)) { filename = DateTime.Now.ToString("yyyyMMddHHmmss"); }
            if (filename.Contains(".png")) { filename = filename.Replace(".png", ""); }
            if (optipons == null) optipons = new Dictionary<string, string>();

            using (Indigo ido = new Indigo())
            {
                try
                {
                    IndigoRenderer ir = new IndigoRenderer(ido);
                    if (optipons.Count > 0)
                    {
                        foreach (var opt in optipons)
                        {
                            ido.setOption(opt.Key, opt.Value);
                        }
                        #region 默认参数
                        if (!optipons.ContainsKey("render-image-size"))
                        {
                            ido.setOption("render-image-size", w + "," + h);
                        }
                        if (!optipons.ContainsKey("render-margins"))
                        {
                            ido.setOption("render-margins", margin, margin);
                        }
                        if (!optipons.ContainsKey("render-relative-thickness"))
                        {
                            ido.setOption("render-relative-thickness", thickness);
                        }
                        #endregion
                    }
                    else
                    {
                        ido.setOption("render-image-size", w + "," + h);
                        ido.setOption("render-margins", margin, margin);
                        ido.setOption("render-relative-thickness", thickness);
                    }

                    #region 默认参数
                    if (def)
                    {
                        if (!optipons.ContainsKey("molfile-saving-no-chiral"))
                        {
                            ido.setOption("molfile-saving-no-chiral", true);//不显示chiral 字样
                        }
                        if (!optipons.ContainsKey("render-stereo-style"))
                        {
                            ido.setOption("render-stereo-style", "none");
                        }
                        if (!optipons.ContainsKey("render-label-mode"))
                        {
                            ido.setOption("render-label-mode", "hetero");
                        }
                        if (!optipons.ContainsKey("render-output-format"))
                        {
                            ido.setOption("render-output-format", "png");//图片格式
                        }
                        if (!optipons.ContainsKey("render-coloring"))
                        {
                            ido.setOption("render-coloring", true);//启用原子着色，例如氮为蓝色，氧为红色等
                        }
                        if (!optipons.ContainsKey("render-background-color"))
                        {
                            ido.setOption("render-background-color", "255,255,255");//背景颜色默认 白色
                        }
                        if (!optipons.ContainsKey("render-implicit-hydrogens-visible"))
                        {
                            ido.setOption("render-implicit-hydrogens-visible", true);//显示可见原子上的隐式氢
                        }
                        if (!optipons.ContainsKey("treat-x-as-pseudoatom"))
                        {
                            ido.setOption("treat-x-as-pseudoatom", "true");
                        }
                        if (!optipons.ContainsKey("render-highlight-thickness-enabled"))
                        {
                            ido.setOption("render-highlight-thickness-enabled", "true");
                        }

                        #region other 
                        //if (!optipons.ContainsKey("treat-x-as-pseudoatom"))
                        //{
                        //    ido.setOption("treat-x-as-pseudoatom", "true");
                        //}
                        //if (!optipons.ContainsKey("molfile-saving-no-chiral"))
                        //{
                        //    ido.setOption("molfile-saving-no-chiral", true);//不显示chiral 字样
                        //}
                        //if (!optipons.ContainsKey("ignore-stereochemistry-errors"))
                        //{
                        //    ido.setOption("ignore-stereochemistry-errors", true);
                        //}
                        //if (!optipons.ContainsKey("ignore-noncritical-query-features"))
                        //{
                        //    ido.setOption("ignore-noncritical-query-features", true);
                        //}
                        #endregion
                    }
                    #endregion

                    // 加载分子
                    IndigoObject molecule = ido.loadMolecule(molorsmiles);
                    if (molecule != null)
                    {
                        using (var bitmap = ir.renderToBitmap(molecule))
                        {
                            if (bitmap != null)
                            {
                                if (!System.IO.Directory.Exists(filepath))
                                {
                                    System.IO.Directory.CreateDirectory(filepath);
                                }

                                var filefullpath = $"{filepath}{filename}.png";
                                if (!string.IsNullOrWhiteSpace(watermarkpath))
                                {
                                    // 加载水印图
                                    using (var watermarkImage = (System.Drawing.Bitmap)System.Drawing.Image.FromFile(watermarkpath))
                                    {
                                        // 创建一个新图层，大小与原图相同
                                        using (var bitmap_wm = new System.Drawing.Bitmap(bitmap.Width, bitmap.Height))
                                        {
                                            using (var graphics = System.Drawing.Graphics.FromImage(bitmap_wm))
                                            {
                                                // 在新图层上绘制原图
                                                graphics.DrawImage(bitmap, 0, 0);
                                                // 设置水印位置（右上角）
                                                var position = new System.Drawing.Point(10, 10);
                                                if (w <= 150)
                                                {
                                                    position = new System.Drawing.Point(5, 5);
                                                }
                                                else if (w <= 300)
                                                {
                                                    position = new System.Drawing.Point(8, 8);
                                                }
                                                // 绘制水印图像
                                                graphics.DrawImage(watermarkImage, position);
                                                // 保存新图像
                                                bitmap_wm.Save(filefullpath);
                                                return filefullpath;
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    bitmap.Save(filefullpath);
                                    return filefullpath;
                                }

                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    errmsg = $"生成结构式图片异常：{ex.Message} >> {ex.StackTrace}";
                }
            }
            return null;
        }
        #endregion

        #region 生成结构式图片
        private static Indigo indigo = null;
        private static IndigoRenderer indigorenderer = null;
        private static int indigo_w = 500;
        //private static int indigo_h = 500;
        //private static int indigo_margin = 10;
        /// <summary>
        /// 结构式构造函数
        /// 常用参数详见：https://lifescience.opensource.epam.com/indigo_this/options/rendering-options.html
        /// </summary>
        /// <param name="w">宽度 默认500</param>
        /// <param name="h">高度 默认500</param>
        /// <param name="margin"> 默认10</param>
        /// <param name="thickness">线条粗度 默认1.5</param>
        /// <param name="optipons">配置参数</param>
        /// <param name="def">是否默认参数 默认true</param>
        public ChemMolecule(int w = 500, int h = 500, int margin = 10, string thickness = "1.5", Dictionary<string, string> optipons = null, bool def = true)
        {
            if (w <= 0) w = 500;
            if (h <= 0) h = 500;
            if (margin <= 0) margin = 10;
            if (string.IsNullOrWhiteSpace(thickness)) { thickness = "1.5"; }
            if (optipons == null) optipons = new Dictionary<string, string>();
            indigo_w = w;
            //indigo_h = h;
            //indigo_margin = margin;

            indigo = new Indigo();
            indigorenderer = new IndigoRenderer(indigo);
            if (optipons.Count > 0)
            {
                foreach (var opt in optipons)
                {
                    indigo.setOption(opt.Key, opt.Value);
                }
                #region 默认参数
                if (!optipons.ContainsKey("render-image-size"))
                {
                    indigo.setOption("render-image-size", w + "," + h);
                }
                if (!optipons.ContainsKey("render-margins"))
                {
                    indigo.setOption("render-margins", margin, margin);
                }
                if (!optipons.ContainsKey("render-relative-thickness"))
                {
                    indigo.setOption("render-relative-thickness", thickness);
                }
                #endregion
            }
            else
            {
                indigo.setOption("render-image-size", w + "," + h);
                indigo.setOption("render-margins", margin, margin);
                indigo.setOption("render-relative-thickness", thickness);
            }

            #region 默认参数
            if (def)
            {
                if (!optipons.ContainsKey("molfile-saving-no-chiral"))
                {
                    indigo.setOption("molfile-saving-no-chiral", true);//不显示chiral 字样
                }
                if (!optipons.ContainsKey("render-stereo-style"))
                {
                    indigo.setOption("render-stereo-style", "none");
                }
                if (!optipons.ContainsKey("render-label-mode"))
                {
                    indigo.setOption("render-label-mode", "hetero");
                }
                if (!optipons.ContainsKey("render-output-format"))
                {
                    indigo.setOption("render-output-format", "png");//图片格式
                }
                if (!optipons.ContainsKey("render-coloring"))
                {
                    indigo.setOption("render-coloring", true);//启用原子着色，例如氮为蓝色，氧为红色等
                }
                if (!optipons.ContainsKey("render-background-color"))
                {
                    indigo.setOption("render-background-color", "255,255,255");//背景颜色默认 白色
                }
                if (!optipons.ContainsKey("render-implicit-hydrogens-visible"))
                {
                    indigo.setOption("render-implicit-hydrogens-visible", false);//不显示可见原子上的隐式氢
                }
                if (!optipons.ContainsKey("treat-x-as-pseudoatom"))
                {
                    indigo.setOption("treat-x-as-pseudoatom", "true");
                }
                if (!optipons.ContainsKey("render-highlight-thickness-enabled"))
                {
                    indigo.setOption("render-highlight-thickness-enabled", "true");
                }

                #region other 
                //if (!optipons.ContainsKey("treat-x-as-pseudoatom"))
                //{
                //    ido.setOption("treat-x-as-pseudoatom", "true");
                //}
                //if (!optipons.ContainsKey("molfile-saving-no-chiral"))
                //{
                //    ido.setOption("molfile-saving-no-chiral", true);//不显示chiral 字样
                //}
                //if (!optipons.ContainsKey("ignore-stereochemistry-errors"))
                //{
                //    ido.setOption("ignore-stereochemistry-errors", true);
                //}
                //if (!optipons.ContainsKey("ignore-noncritical-query-features"))
                //{
                //    ido.setOption("ignore-noncritical-query-features", true);
                //}
                #endregion
            }
            #endregion
        }

        /// <summary>
        /// 生成结构式图片(with ImageSharp)
        /// </summary>
        /// <param name="molorsmiles">mol或smiles</param>
        /// <param name="errmsg">返回错误信息</param>
        /// <param name="timemsg">耗时信息</param>
        /// <param name="opacity">水印透明度度 默认80%</param>
        /// <param name="filename">生成图片名称，不含.png 默认当前时间</param>
        /// <param name="filepath">存放路径 默认当前根目录下images目录</param>
        /// <param name="watermarkpath">水印图片路径，无则说明无水印 默认无</param>
        /// <param name="othersize">其他尺寸（长宽一致） 默认无</param>
        /// <returns>生成图片的全路径，若为空则说明生成失败</returns>
        public string SaveToImage(string molorsmiles, out string errmsg, out string timemsg, float opacity = 0.8f, string filename = "structimg", string filepath = "", string watermarkpath = "", int[] othersize = null, bool dolayout = true)
        {
            errmsg = "";
            timemsg = "";
            if (string.IsNullOrWhiteSpace(molorsmiles)) { errmsg = "Mol或Smiles文件为空，无法生成结构式图片！"; return null; }
            if (string.IsNullOrWhiteSpace(filepath)) { filepath = AppDomain.CurrentDomain.BaseDirectory + "images\\"; }
            if (string.IsNullOrWhiteSpace(filename)) { filename = DateTime.Now.ToString("yyyyMMddHHmmss"); }
            if (filename.Contains(".png")) { filename = filename.Replace(".png", ""); }
            if (indigo == null || indigorenderer == null) { errmsg = "indigo对象未实例化！"; return null; }
            try
            {
                DateTime btime = DateTime.Now;
                // 加载分子
                IndigoObject molecule = indigo.loadMolecule(molorsmiles);
                if (molecule != null)
                {
                    if (dolayout)
                    {
                        // 清理分子以优化显示（重新计算 2D 坐标）
                        molecule.layout();
                    }
                    var countImplicitHydrogens = molecule.countImplicitHydrogens();
                    timemsg += $"[隐式氢个数:{countImplicitHydrogens}]";
                    var bytes = indigorenderer.renderToBuffer(molecule);
                    if (bytes != null && bytes.Length > 0)
                    {
                        if (!System.IO.Directory.Exists(filepath))
                        {
                            System.IO.Directory.CreateDirectory(filepath);
                        }
                        timemsg += $"loadMolecule耗时{DateTime.Now.Subtract(btime).TotalMilliseconds}毫秒；";
                        var filefullpath = $"{filepath}{filename}.png";
                        if (!string.IsNullOrWhiteSpace(watermarkpath))
                        {
                            DateTime btime_tmp = DateTime.Now;
                            using (var image = SixLabors.ImageSharp.Image.Load(bytes))
                            {
                                // 加载水印图
                                using (var watermark = SixLabors.ImageSharp.Image.Load(watermarkpath))
                                {
                                    // 计算水印的位置（左上角）
                                    int x = 10;
                                    int y = 10;
                                    // 将水印绘制到原始图片上
                                    image.Mutate(ctx => ctx.DrawImage(watermark, new SixLabors.ImageSharp.Point(x, y), opacity)); // 0.8f 是水印的透明度
                                }
                                timemsg += $"加水印耗时{DateTime.Now.Subtract(btime_tmp).TotalMilliseconds}毫秒；";
                                btime_tmp = DateTime.Now;
                                image.Save(filefullpath, new PngEncoder());
                                timemsg += $"保存图片耗时{DateTime.Now.Subtract(btime_tmp).TotalMilliseconds}毫秒；";
                            }
                        }
                        else
                        {
                            using (var image = SixLabors.ImageSharp.Image.Load(bytes))
                            {
                                image.Save(filefullpath, new PngEncoder());
                            }
                            timemsg += $"保存图片耗时{DateTime.Now.Subtract(btime).TotalMilliseconds}毫秒；";
                        }
                        if (othersize != null && othersize.Length > 0)
                        {
                            DateTime btime_tmp = DateTime.Now;
                            foreach (var size in othersize)
                            {
                                using (var image = SixLabors.ImageSharp.Image.Load(filefullpath))
                                {
                                    // 缩放图片到指定尺寸
                                    image.Mutate(ctx => ctx.Resize(new SixLabors.ImageSharp.Size(size, size)));
                                    image.Save(filefullpath.Replace($"x{indigo_w}", $"x{size}"));
                                }
                            }
                            timemsg += $"保存其他尺寸图片耗时{DateTime.Now.Subtract(btime_tmp).TotalMilliseconds}毫秒；";
                        }
                        timemsg += $"总耗时{DateTime.Now.Subtract(btime).TotalMilliseconds}毫秒。";
                        return filefullpath;
                    }
                }
            }
            catch (Exception ex)
            {
                errmsg = $"生成结构式图片异常：{ex.Message} >> {ex.StackTrace}";
            }
            return null;
        }
        /// <summary>
        /// 生成结构式图片Byte(with ImageSharp)
        /// </summary>
        /// <param name="molorsmiles">mol或smiles</param>
        /// <param name="errmsg">返回错误信息</param>
        /// <param name="timemsg">耗时信息</param>
        /// <param name="opacity">水印透明度度 默认80%</param>
        /// <param name="filename">生成图片名称，不含.png 默认当前时间</param>
        /// <param name="filepath">存放路径 默认当前根目录下images目录</param>
        /// <param name="watermarkpath">水印图片路径，无则说明无水印 默认无</param>
        /// <param name="othersize">其他尺寸（长宽一致） 默认无</param>
        /// <param name="othersizes">其他尺寸（长宽不一致） 默认无 格式：长,宽;长,宽;如：605,375;242,150;121,75;220,220;</param>
        /// <returns>生成图片的全路径，若为空则说明生成失败</returns>
        public byte[] SaveToImageByte(string molorsmiles, out string errmsg, out string timemsg, float opacity = 0.8f, string watermarkpath = "")
        {
            errmsg = "";
            timemsg = "";
            if (string.IsNullOrWhiteSpace(molorsmiles)) { errmsg = "Mol或Smiles文件为空，无法生成结构式图片！"; return null; }
            if (indigo == null || indigorenderer == null) { errmsg = "indigo对象未实例化！"; return null; }
            try
            {
                var othersizelist = new List<string>();
                DateTime btime = DateTime.Now;
                // 加载分子
                IndigoObject molecule = indigo.loadMolecule(molorsmiles);
                if (molecule != null)
                {
                    byte[] picbytes = null;
                    var bytes = indigorenderer.renderToBuffer(molecule);
                    if (bytes != null && bytes.Length > 0)
                    {
                        timemsg += $"loadMolecule耗时{DateTime.Now.Subtract(btime).TotalMilliseconds}毫秒；";
                        if (!string.IsNullOrWhiteSpace(watermarkpath))
                        {
                            DateTime btime_tmp = DateTime.Now;
                            using (var image = SixLabors.ImageSharp.Image.Load(bytes))
                            {
                                // 加载水印图
                                using (var watermark = SixLabors.ImageSharp.Image.Load(watermarkpath))
                                {
                                    // 计算水印的位置（左上角）
                                    int x = 10;
                                    int y = 10;
                                    // 将水印绘制到原始图片上
                                    image.Mutate(ctx => ctx.DrawImage(watermark, new SixLabors.ImageSharp.Point(x, y), opacity)); // 0.8f 是水印的透明度
                                }
                                timemsg += $"加水印耗时{DateTime.Now.Subtract(btime_tmp).TotalMilliseconds}毫秒；";
                                btime_tmp = DateTime.Now;
                                using (var ms = new MemoryStream())
                                {
                                    image.Save(ms, new PngEncoder());
                                    picbytes = ms.ToArray();
                                }
                                timemsg += $"保存图片耗时{DateTime.Now.Subtract(btime_tmp).TotalMilliseconds}毫秒；";
                            }
                        }
                        else
                        {
                            timemsg += $"保存图片耗时{DateTime.Now.Subtract(btime).TotalMilliseconds}毫秒；";
                            picbytes = bytes;
                        }
                        timemsg += $"总耗时{DateTime.Now.Subtract(btime).TotalMilliseconds}毫秒。";
                        return picbytes;
                    }
                }
            }
            catch (Exception ex)
            {
                errmsg = $"生成结构式图片异常：{ex.Message} >> {ex.StackTrace}";
            }
            return null;
        }
        /// <summary>
        /// 生成结构式图片
        /// </summary>
        /// <param name="molorsmiles">mol或smiles</param>
        /// <param name="errmsg">返回错误信息</param>
        /// <param name="w">宽度 默认500</param>
        /// <param name="filename">生成图片名称，不含.png 默认当前时间</param>
        /// <param name="filepath">存放路径 默认当前根目录下images目录</param>
        /// <param name="watermarkpath">水印图片路径，无则说明无水印 默认无</param>
        /// <returns>生成图片的全路径，若为空则说明生成失败</returns>
        public string SaveToImageBitmap(string molorsmiles, out string errmsg, int w = 500, string filename = "structimg", string filepath = "", string watermarkpath = "")
        {
            errmsg = "";
            if (string.IsNullOrWhiteSpace(molorsmiles)) { errmsg = "Mol或Smiles文件为空，无法生成结构式图片！"; return null; }
            if (string.IsNullOrWhiteSpace(filepath)) { filepath = AppDomain.CurrentDomain.BaseDirectory + "images\\"; }
            if (string.IsNullOrWhiteSpace(filename)) { filename = DateTime.Now.ToString("yyyyMMddHHmmss"); }
            if (filename.Contains(".png")) { filename = filename.Replace(".png", ""); }
            if (indigo == null || indigorenderer == null) { errmsg = "indigo对象未实例化！"; return null; }
            try
            {
                // 加载分子
                IndigoObject molecule = indigo.loadMolecule(molorsmiles);
                if (molecule != null)
                {
                    using (var bitmap = indigorenderer.renderToBitmap(molecule))
                    {
                        if (bitmap != null)
                        {
                            if (!System.IO.Directory.Exists(filepath))
                            {
                                System.IO.Directory.CreateDirectory(filepath);
                            }

                            var filefullpath = $"{filepath}{filename}.png";
                            if (!string.IsNullOrWhiteSpace(watermarkpath))
                            {
                                // 加载水印图
                                using (var watermarkImage = (System.Drawing.Bitmap)System.Drawing.Image.FromFile(watermarkpath))
                                {
                                    // 创建一个新图层，大小与原图相同
                                    using (var bitmap_wm = new System.Drawing.Bitmap(bitmap.Width, bitmap.Height))
                                    {
                                        using (var graphics = System.Drawing.Graphics.FromImage(bitmap_wm))
                                        {
                                            // 在新图层上绘制原图
                                            graphics.DrawImage(bitmap, 0, 0);
                                            // 设置水印位置（右上角）
                                            var position = new System.Drawing.Point(10, 10);
                                            if (w <= 150)
                                            {
                                                position = new System.Drawing.Point(5, 5);
                                            }
                                            else if (w <= 300)
                                            {
                                                position = new System.Drawing.Point(8, 8);
                                            }
                                            // 绘制水印图像
                                            graphics.DrawImage(watermarkImage, position);
                                            // 保存新图像
                                            bitmap_wm.Save(filefullpath);
                                            return filefullpath;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                bitmap.Save(filefullpath);
                                return filefullpath;
                            }

                        }
                    }
                }
            }
            catch (Exception ex)
            {
                errmsg = $"生成结构式图片异常：{ex.Message} >> {ex.StackTrace}";
            }
            return null;
        }





        private Indigo indigo_this = null;
        private IndigoRenderer indigorenderer_this = null;
        /// <summary>
        /// 结构式构造函数
        /// 常用参数详见：https://lifescience.opensource.epam.com/indigo_this/options/rendering-options.html
        /// </summary>
        /// <param name="w">宽度 默认500</param>
        /// <param name="h">高度 默认500</param>
        /// <param name="margin"> 默认10</param>
        /// <param name="thickness">线条粗度 默认1.5</param>
        /// <param name="optipons">配置参数</param>
        /// <param name="def">是否默认参数 默认true</param>
        public ChemMolecule(Indigo _indigo, IndigoRenderer _indigoobject, int w = 500, int h = 500, int margin = 10, string thickness = "1.5", Dictionary<string, string> optipons = null, bool def = true)
        {
            if (w <= 0) w = 500;
            if (h <= 0) h = 500;
            if (margin <= 0) margin = 10;
            if (string.IsNullOrWhiteSpace(thickness)) { thickness = "1.5"; }
            if (optipons == null) optipons = new Dictionary<string, string>();

            indigo_this = _indigo;
            indigorenderer_this = _indigoobject;
            if (optipons.Count > 0)
            {
                foreach (var opt in optipons)
                {
                    indigo_this.setOption(opt.Key, opt.Value);
                }
                #region 默认参数
                if (!optipons.ContainsKey("render-image-size"))
                {
                    indigo_this.setOption("render-image-size", w + "," + h);
                }
                if (!optipons.ContainsKey("render-margins"))
                {
                    indigo_this.setOption("render-margins", margin, margin);
                }
                if (!optipons.ContainsKey("render-relative-thickness"))
                {
                    indigo_this.setOption("render-relative-thickness", thickness);
                }
                #endregion
            }
            else
            {
                indigo_this.setOption("render-image-size", w + "," + h);
                indigo_this.setOption("render-margins", margin, margin);
                indigo_this.setOption("render-relative-thickness", thickness);
            }

            #region 默认参数
            if (def)
            {
                if (!optipons.ContainsKey("molfile-saving-no-chiral"))
                {
                    indigo_this.setOption("molfile-saving-no-chiral", true);//不显示chiral 字样
                }
                if (!optipons.ContainsKey("render-stereo-style"))
                {
                    indigo_this.setOption("render-stereo-style", "none");
                }
                if (!optipons.ContainsKey("render-label-mode"))
                {
                    indigo_this.setOption("render-label-mode", "hetero");
                }
                if (!optipons.ContainsKey("render-output-format"))
                {
                    indigo_this.setOption("render-output-format", "png");//图片格式
                }
                if (!optipons.ContainsKey("render-coloring"))
                {
                    indigo_this.setOption("render-coloring", true);//启用原子着色，例如氮为蓝色，氧为红色等
                }
                if (!optipons.ContainsKey("render-background-color"))
                {
                    indigo_this.setOption("render-background-color", "255,255,255");//背景颜色默认 白色
                }
                if (!optipons.ContainsKey("render-implicit-hydrogens-visible"))
                {
                    indigo_this.setOption("render-implicit-hydrogens-visible", true);//显示可见原子上的隐式氢
                }
                if (!optipons.ContainsKey("treat-x-as-pseudoatom"))
                {
                    indigo_this.setOption("treat-x-as-pseudoatom", "true");
                }
                if (!optipons.ContainsKey("render-highlight-thickness-enabled"))
                {
                    indigo_this.setOption("render-highlight-thickness-enabled", "true");
                }

                #region other 
                //if (!optipons.ContainsKey("treat-x-as-pseudoatom"))
                //{
                //    ido.setOption("treat-x-as-pseudoatom", "true");
                //}
                //if (!optipons.ContainsKey("molfile-saving-no-chiral"))
                //{
                //    ido.setOption("molfile-saving-no-chiral", true);//不显示chiral 字样
                //}
                //if (!optipons.ContainsKey("ignore-stereochemistry-errors"))
                //{
                //    ido.setOption("ignore-stereochemistry-errors", true);
                //}
                //if (!optipons.ContainsKey("ignore-noncritical-query-features"))
                //{
                //    ido.setOption("ignore-noncritical-query-features", true);
                //}
                #endregion
            }
            #endregion
        }

        /// <summary>
        /// 生成结构式图片Byte(with ImageSharp 传入indigo对象)
        /// </summary>
        /// <param name="molorsmiles">mol或smiles</param>
        /// <param name="_indigo">indigo_this</param>
        /// <param name="_indigorenderer">indigorenderer_this</param>
        /// <param name="errmsg">返回错误信息</param>
        /// <param name="timemsg">耗时信息</param>
        /// <param name="opacity">水印透明度度 默认80%</param>
        /// <param name="watermarkpath">水印图片路径，无则说明无水印 默认无</param>
        /// <returns>生成图片的全路径，若为空则说明生成失败</returns>
        public byte[] SaveToImageByteThis(string molorsmiles, out string errmsg, out string timemsg, float opacity = 0.8f, string watermarkpath = "")
        {
            errmsg = "";
            timemsg = "";
            if (string.IsNullOrWhiteSpace(molorsmiles)) { errmsg = "Mol或Smiles文件为空，无法生成结构式图片！"; return null; }
            if (indigo_this == null || indigorenderer_this == null) { errmsg = "indigo对象未实例化！"; return null; }
            try
            {
                var othersizelist = new List<string>();
                DateTime btime = DateTime.Now;
                // 加载分子
                IndigoObject molecule = indigo_this.loadMolecule(molorsmiles);
                if (molecule != null)
                {
                    byte[] picbytes = null;
                    var bytes = indigorenderer_this.renderToBuffer(molecule);
                    if (bytes != null && bytes.Length > 0)
                    {
                        timemsg += $"loadMolecule耗时{DateTime.Now.Subtract(btime).TotalMilliseconds}毫秒；";
                        if (!string.IsNullOrWhiteSpace(watermarkpath))
                        {
                            DateTime btime_tmp = DateTime.Now;
                            using (var image = SixLabors.ImageSharp.Image.Load(bytes))
                            {
                                // 加载水印图
                                using (var watermark = SixLabors.ImageSharp.Image.Load(watermarkpath))
                                {
                                    // 计算水印的位置（左上角）
                                    int x = 10;
                                    int y = 10;
                                    // 将水印绘制到原始图片上
                                    image.Mutate(ctx => ctx.DrawImage(watermark, new SixLabors.ImageSharp.Point(x, y), opacity)); // 0.8f 是水印的透明度
                                }
                                timemsg += $"加水印耗时{DateTime.Now.Subtract(btime_tmp).TotalMilliseconds}毫秒；";
                                btime_tmp = DateTime.Now;
                                using (var ms = new MemoryStream())
                                {
                                    image.Save(ms, new PngEncoder());
                                    picbytes = ms.ToArray();
                                }
                                timemsg += $"保存图片耗时{DateTime.Now.Subtract(btime_tmp).TotalMilliseconds}毫秒；";
                            }
                        }
                        else
                        {
                            timemsg += $"保存图片耗时{DateTime.Now.Subtract(btime).TotalMilliseconds}毫秒；";
                            picbytes = bytes;
                        }
                        timemsg += $"总耗时{DateTime.Now.Subtract(btime).TotalMilliseconds}毫秒。";
                        return picbytes;
                    }
                }
            }
            catch (Exception ex)
            {
                errmsg = $"生成结构式图片异常：{ex.Message} >> {ex.StackTrace}";
            }
            return null;
        }
        #endregion


        #region 通过MOL/SMILES获取结构式信息
        /// <summary>
        /// 通过MOL/SMILES获取结构式信息
        /// </summary>
        /// <param name="molorsmiles">支持MOL，SMILES</param>
        /// <returns></returns>
        public static ChemMolecule GetMolecule(string molorsmiles)
        {
            return GetMolecule(molorsmiles, null);
        }
        /// <summary>
        /// 通过MOL/SMILES获取结构式信息
        /// </summary>
        /// <param name="molorsmiles">支持MOL，SMILES</param>
        /// <param name="optipons">自定义参数 详情见：https://lifescience.opensource.epam.com/indigo_this/options/rendering-options.html </param>
        /// <returns></returns>
        public static ChemMolecule GetMolecule(string molorsmiles, Dictionary<string, string> optipons)
        {
            string errmsg = string.Empty;
            return GetMolecule(molorsmiles, optipons, ref errmsg);
        }
        /// <summary>
        /// 通过MOL/SMILES获取结构式信息
        /// </summary>
        /// <param name="molorsmiles">支持MOL，SMILES</param>
        /// <param name="optipons">自定义参数 详情见：https://lifescience.opensource.epam.com/indigo_this/options/rendering-options.html </param>
        /// <param name="errmsg">错误消息</param>
        /// <returns></returns>
        public static ChemMolecule GetMolecule(string molorsmiles, Dictionary<string, string> optipons, ref string errmsg)
        {
            Indigo ido = new Indigo();
            try
            {
                IndigoRenderer ir = new IndigoRenderer(ido);
                molorsmiles = FormatSMILES(molorsmiles);
                if (optipons != null && optipons.Count > 0)
                {
                    foreach (var opt in optipons)
                    {
                        ido.setOption(opt.Key, opt.Value);
                    }
                }
                IndigoObject io = ido.loadMolecule(molorsmiles);
                ChemMolecule mol = null;
                if (io != null)
                {
                    mol = new ChemMolecule(ido, io);
                }
                return mol;
            }
            catch (Exception ex)
            {
                errmsg = "异常：" + ex.Message;
                return null;
            }
        }
        #endregion

        /// <summary>
        /// 打开帮助文档
        /// https://lifescience.opensource.epam.com/indigo/options/rendering-options.html
        /// </summary>
        public static void Help()
        {
            System.Diagnostics.Process.Start("https://lifescience.opensource.epam.com/indigo/options/rendering-options.html");
        }
    }
}
